/*
 	Tool for Path Finding Algorithm Testing (ToPFAT) 
 	
    Copyright (C) 2009, Teemu Turunen and Arttu Viljakainen 
    Published under GNU General Public License V3 
    (see http://www.gnu.org/licenses/licenses.html)
    
    This file is part of Tool for Path Finding Algorithm Testing.

    Tool for Path Finding Algorithm Testing is free software: you can 
    redistribute it and/or modify it under the terms of the GNU General 
    Public License as published by the Free Software Foundation, 
    either version 3 of the License, or (at your option) any later version.

    Tool for Path Finding Algorithm Testing is distributed in the hope that it
    will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 
    Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Tool for Path Finding Algorithm Testing. If not, see         <http://www.gnu.org/licenses/>.
 */

package pathfinding.gui;

import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Point;
import java.util.Random;

import javax.swing.JPanel;

import pathfinding.CellTerrainType;
import algorithms.DescriptedPoint;
import algorithms.SearchResult;

/**
 * Class to represent map on the screen. Map is a JPanel containgin 
 * cells that are JLabels.
 * 
 * @author arttu viljakainen
 *
 */
public class Map extends JPanel {
	
	private final int rows;
	private final int columns;
	private GridLayout mapGrid;
	private MapCell startCell;
	private MapCell goalCell;
	
	/**
	 * 
	 * @param columns
	 * @param rows
	 */
	public Map(int columns, int rows) {
		this.rows = rows;
		this.columns = columns;
		
		mapGrid = new GridLayout(rows, columns);
		mapGrid.setHgap(1);
		mapGrid.setVgap(1);
		
		this.setLayout(this.mapGrid);
		this.setOpaque(true);
		this.setBackground(Color.DARK_GRAY);
		
		startCell = null;
		goalCell = null;
	}
	
	public int[][] getAsArraymap() {
		int[][] arrayMap = new int[this.getRows()][this.getColumns()];
		
		for(int rowIndex = 0; rowIndex < this.getRows(); rowIndex ++) {
			for(int columnIndex = 0; columnIndex < this.getColumns(); columnIndex ++) {
				if(this.getMapCell(rowIndex, columnIndex).isWalkable())
					arrayMap[rowIndex][columnIndex] = 0;
				else
					arrayMap[rowIndex][columnIndex] = 1;
			}
		}
		
		return arrayMap;
	}
	
	/**
	 * Returns copy of this map (only terrain and path end points).
	 * 
	 * @return Copy of map.
	 */
	public Map getCopyOfMapTerrain() {
		Map map = new Map(getColumns(), getRows());
		for(Component com : (Component[])getComponents()) {
			if(com instanceof MapCell) { 
				MapCell cell = (MapCell)com;
				map.add((MapCell)cell.clone());
			}
			
		}
		
		if(startCell != null) {
			map.startCell = map.getMapCell(startCell.getLocation().y, startCell.getLocation().x);
			map.setStartCellTo(map.startCell);
		}
			
		if(goalCell != null) {
			map.goalCell = map.getMapCell(goalCell.getLocation().y, goalCell.getLocation().x);
			map.setGoalCellTo(map.goalCell);
		}
			
		return map;
	}
	
	/**
	 * Sets given searchResults to be visible
	 * on the screen.
	 * 
	 * @param searchResults
	 */
	public void setSearchResultsToMap(SearchResult searchResults) {
		if(searchResults == null) 
			return;
		for(DescriptedPoint unusedCandidate: searchResults.getUnUsedCandidates()) {
			MapCell cell = getMapCell(unusedCandidate.y, unusedCandidate.x);
			cell.setUnusedCandidateCell();
			cell.setToolTip(unusedCandidate.getDescription());
		}
		for(DescriptedPoint usedCandidate: searchResults.getUsedCandidates()) {
			MapCell cell = getMapCell(usedCandidate.y, usedCandidate.x);
			cell.setUsedCandidateCell();
			cell.setToolTip(usedCandidate.getDescription());
		}
		for(DescriptedPoint pathCell: searchResults.getPath()) {
			MapCell cell = getMapCell(pathCell.y, pathCell.x);
			cell.setPathCell();
			cell.setToolTip(pathCell.getDescription());
			
		}
		startCell.setStartCell();
		goalCell.setGoalCell();
	}
	
	/**
	 * Clears search results.
	 */
	public void clearSearchResultsAndPathEndpoints() {
		for(int rowIndex = 0; rowIndex < rows; rowIndex ++) {
			for(int columnIndex = 0; columnIndex < columns; columnIndex ++) {
				MapCell cell = getMapCell(rowIndex, columnIndex);
				cell.setNothingCell();
			}
		}
		this.startCell = null;
		this.goalCell = null;
	}
	
	/**
	 * Clears search results but keeps the path end points. 
	 */
	public void clearSearchResultsWithoutPathEndpoints() {
		for(int rowIndex = 0; rowIndex < rows; rowIndex ++) {
			for(int columnIndex = 0; columnIndex < columns; columnIndex ++) {				
				MapCell cell = getMapCell(rowIndex, columnIndex);
					cell.setNothingCell();
			}
		}
		if(startCell != null) 
			setStartCellTo(startCell);
		if(goalCell != null)
			setGoalCellTo(goalCell);
	}
	
	/**
	 * Returns the cost for the cell.
	 * 
	 * @param rowIndex
	 * @param columnIndex
	 * @return cost for the cell
	 */
	public int getCostForCell(int rowIndex, int columnIndex) {
		return getMapCell(rowIndex, columnIndex).getCost();
	}
			
	/**
	 * Sets the start cell for the map.
	 * 
	 * @param cell
	 */
	public void setStartCellTo(MapCell cell) {
		if(startCell != null) 
			startCell.setNothingCell();
		(startCell = cell).setStartCell();
	}
	
	/**
	 * Sets the goal cell for the map.	
	 * 
	 * @param cell
	 */
	public void setGoalCellTo(MapCell cell) {
		if(goalCell != null)
			goalCell.setNothingCell();
		(goalCell = cell).setGoalCell();
	}
	
	/**
	 * Clears special characteristics from the cell.
	 * 
	 * @param mapCell
	 */
	public void setAsNotSpecialCell(MapCell mapCell) {
		mapCell.setNothingCell();
		if(startCell != null && startCell.equals(mapCell))
			startCell = null;
		else if(goalCell != null && goalCell.equals(mapCell))
			goalCell = null;
	}
	
	/**
	 * Returns the cell in specific position.
	 * 
	 * @param rowIndex
	 * @param columnIndex
	 * @return map cell
	 */
	public MapCell getMapCell(int rowIndex, int columnIndex) {
		int index = columnIndex + rowIndex * columns;
		return ((MapCell)this.getComponent(index));
	}
	
	/**
	 * Creates a random map.
	 */
	public void randomMap() {
		for(int rowIndex = 0; rowIndex < rows; rowIndex ++) {
			for(int columnIndex = 0; columnIndex < columns; columnIndex ++) {
				randomCell(rowIndex, columnIndex);
			}
		}
	}
	
	/**
	 * Set the cell randomly.
	 * 
	 * @param rowIndex
	 * @param columnIndex
	 */
	private void randomCell(int rowIndex, int columnIndex) {
		Random random = new Random();
		
		if(rowIndex == 0 || rowIndex == rows - 1 || 
				columnIndex == 0 || columnIndex == columns - 1) {
			this.add(new MapCell(CellTerrainType.WALL, new Point(columnIndex, rowIndex)));

		} else {
			if(random.nextInt(10) >= 7) {
				this.add(new MapCell(CellTerrainType.WALL, new Point(columnIndex, rowIndex)));
			} else {
				this.add(new MapCell(CellTerrainType.FLOOR, new Point(columnIndex, rowIndex)));
			}
		}
	}
	
	/**
	 * Clears the map.
	 */
	public void clearMap() {
		for(int rowIndex = 0; rowIndex < rows; rowIndex ++) {
			for(int columnIndex = 0; columnIndex < columns; columnIndex ++) {
				if(rowIndex == 0 || rowIndex == rows - 1 || 
						columnIndex == 0 || columnIndex == columns - 1) {
					this.add(new MapCell(CellTerrainType.WALL, new Point(columnIndex, rowIndex)));
				} else {
					this.add(new MapCell(CellTerrainType.FLOOR, new Point(columnIndex, rowIndex)));
				}
			}
		}

	}
	
	public int getRows() {
		return rows;
	}

	public int getColumns() {
		return columns;
	}

	public MapCell getStartCell() {
		return startCell;
	}

	public MapCell getGoalCell() {
		return goalCell;
	}

}
